package com.peter.image.manage.fs;

import java.io.File;
import java.io.FileFilter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class FsManager {
    private static Logger logger = LogManager.getLogger(FsManager.class);

    @Autowired
    private Environment env;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    // @Autowired
    // private StringRedisTemplate stringRedisTemplate;

    public FsManager() {
        logger.info(">>> 创建FsManager");
    }

    public List<File> listByOneId(Organization org, int id) {
        File[] cacheFiles = this.getFsCache(org, id);

        if(cacheFiles != null) {
            return Arrays.asList(cacheFiles);
        }

        String path = this.getParentPathByName(org, id);
        File folder = new File(path);

        if(!folder.exists() || !folder.isDirectory()) {
            return new ArrayList<File>();
        }

        File[] files = this.listFilesInFolder(folder, true);
        this.updateFsCache(org, id, files);
        return Arrays.asList(files);
    }

    public List<File> listByStartId(Organization org, int startId, int pageSize) {
        List<File> result = new ArrayList<File>();
        int vacantSize = pageSize;

        String firstFolder = String.format("%02d", startId / 10000);
        String secondFolder = String.format("%02d", startId % 10000 / 100);

        String rootPath = this.env.getProperty("image.store.path");
        String path = rootPath + this.getOrgFolderName(org);
        File rootFolder = new File(path);

        File[] files = this.listFilesInFolder(rootFolder, true, true, firstFolder);
        boolean needFilder2 = true;

        loop:
        for(File folder: files) {
            File[] files2 = this.listFilesInFolder(folder, true, needFilder2, secondFolder);

            if(files2.length == 0) {
                continue;
            }

            needFilder2 = false;
            boolean needFilder3 = true;

            for(File folder3: files2) {
                File[] files3 = this.listFilesInFolder(folder3, true, needFilder3,
                        String.valueOf(startId), vacantSize);

                if(files3.length == 0) {
                    continue;
                }

                needFilder3 = false;

                for(File folder4: files3) {
                    result.add(folder4);
                    vacantSize--;

                    if(vacantSize <= 0) {
                        break loop;
                    }
                }
            }
        }

        return result;
    }

    private File[] listFilesInFolder(File folder, boolean mustFolder) {
        return this.listFilesInFolder(folder, mustFolder, false, null);
    }

    private File[] listFilesInFolder(File folder, boolean mustFolder, boolean needFilder,
            String minName)
    {
        return this.listFilesInFolder(folder, mustFolder, needFilder, minName, -1);
    }

    private File[] listFilesInFolder(File folder, boolean mustFolder, boolean needFilder,
            String minName, int size)
    {
        if(folder == null || folder.isFile() || folder.length() == 0) {
            return new File[0];
        }

        File[] files = folder.listFiles(new FolderFilder(mustFolder, needFilder, minName));

        if(files.length == 0) {
            return files;
        }

        Arrays.parallelSort(files, new FileComparator());
        return size > 0 && files.length > size ? Arrays.copyOfRange(files, 0, size) : files;
    }

    public Environment getEnv() {
        return env;
    }

    public void setEnv(Environment env) {
        this.env = env;
    }

    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public String getOrgFolderName(Organization org) {
        switch (org) {
        case MEITURI:
            return "meituri";
        case NVSHENS:
            return "nvshens";
        default:
            return "nvshens";
        }
    }

    public String getPathByName(Organization org, int id) {
        return this.getParentPathByName(org, id) + File.separator + String.valueOf(id);
    }

    public boolean verifyImageSetId(Organization org, int id) {
        return true;
    }

    public String getParentPathByName(Organization org, int id) {
        String firstFolder = String.format("%02d", id / 10000);
        String secondFolder = String.format("%02d", id % 10000 / 100);

        String rootPath = this.env.getProperty("image.store.path");
        return rootPath + this.getOrgFolderName(org) + File.separator + firstFolder
                + File.separator + secondFolder;
    }

    public List<String> getImagesInFolder(String path) {
        File folder = new File(path);

        if(!folder.exists()) {
            return null;
        }

        List<String> result = new ArrayList<String>();
        File[] images = folder.listFiles();
        String rootPath = env.getProperty("image.store.path");

        for(File image: images) {
            String iPath = "/resources" + image.getAbsolutePath().substring(rootPath.length() - 1);
            result.add(iPath);
        }

        return result;
    }

    public boolean deleteFolder(Organization org, int id) {
        String path = this.getPathByName(org, id);
        File folder = new File(path);

        if(!folder.exists()) {
            return true;
        }

        return this.deleteFile(folder);
    }

    private boolean deleteFile(File file) {
        if(file.isDirectory()) {
            for(File f: file.listFiles()) {
                this.deleteFile(f);
            }
        }

        return file.delete();
    }

    public boolean favorite(Organization org, int id) throws IOException {
        File file = this.getFavoriteFile(org);

        try (FileWriter fileWriter = new FileWriter(file, true)) {
            fileWriter.append(id + ", ").flush();
        }

        return true;
    }

    private File getFavoriteFile(Organization org) throws IOException {
        String path = this.env.getProperty("image.store.path") + this.getOrgFolderName(org) +
                File.separator + "favorite.note";

        File file = new File(path);

        if(!file.exists()) {
            file.createNewFile();
        }

        return file;
    }

    @SuppressWarnings("unused")
    public File[] getFsCache(Organization org, int id) {
        String orgName = this.getOrgFolderName(org);
        String redisKey = orgName + "_" + id / 100;
        // JsonFactory factory = new JsonFactory();
        // return (File[]) redisTemplate.opsForHash().get("image_fs", redisKey);
        return null;
    }

    public boolean updateFsCache(Organization org, int id, File[] files) {
        String orgName = this.getOrgFolderName(org);
        String redisKey = orgName + "_" + id / 100;
        System.out.println(this.redisTemplate);
        this.redisTemplate.opsForHash().put("image_fs", redisKey, "test");
        // hashOperations.put("image_fs", redisKey, "test");
        // redisTemplate.opsForHash().put("image_fs", redisKey, files);
        // HashOperations<String, Object, Object> ofh = redisTemplate.opsForHash();
        // ofh.put("test", redisKey, "testV");

        return true;
    }

    public enum Organization {
        MEITURI,
        NVSHENS
    }

    private class FileComparator implements Comparator<File> {
        /**
         * 如果返回1，则o2在o1前面。
         */
        @Override
        public int compare(File o1, File o2) {
            if (o1.isDirectory() && o2.isFile())
                return -1;

            if (o1.isFile() && o2.isDirectory())
                return 1;

            return o1.getName().compareTo(o2.getName());
        }

    }

    private class FolderFilder implements FileFilter {
        private boolean mustFolder;
        private boolean upBrother;
        private String minName;

        public FolderFilder(boolean mustFolder, boolean upBrother, String minName) {
            this.minName = minName;
            this.upBrother = upBrother;
            this.mustFolder = mustFolder;
        }

        @Override
        public boolean accept(File file) {
            if(mustFolder && !file.isDirectory()) {
                return false;
            }

            return file.isDirectory() &&
                    (this.upBrother ? file.getName().compareTo(minName) >= 0 : true);
        }
    }
}
